package com.cindata

import com.cindata.base.*
import com.cindata.dictionary.*
import grails.gorm.transactions.Transactional

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

@Transactional
class EstateBatchEvaluateService
{
    static scope = "singleton"
    private static int batchLimit = 5000
    def apiService
    def evaluationApiService
    def splitMatchApiService
    def dataSource
    def logService

    def threadCount = 0
    def finishThreadCount = 0

    def sessionFactory

    def rootName = EstateBatchEvaluateService.class.getResource("/").getPath();
    List<Map> failedList = new ArrayList<Map>();

    def addsynThreadCount()
    {
        synchronized (finishThreadCount)
        {
            finishThreadCount ++
        }
    }

     /**
     * 批次评估*/
    synchronized void evaluateBatch(Serializable batchId, Integer em)
    {
        EstateImportBatch importBatch = EstateImportBatch.get(batchId)
        int count = BatchEstate.countByBatch(importBatch)
        if (count > 0)
        {
            int batchCounts = (count / 5) + 1
            for (int i = 0; i < 5; i++)
            {
                threadCount  = threadCount + 1
                def estateQuery = BatchEstate.where {
                    batch.id == importBatch.getId()
                }
                def list = estateQuery.list([sort: 'id', offset: i * batchCounts, max: batchCounts])
                def newList = []
                list.each {
                    newList.add(it)
                }
                Thread.start 
                {
                    try 
                    {
                        BatchEstate.withTransaction
                        {
                            evaluate(newList, em, importBatch, count)
                        }    
                    }
                    catch(Exception e) 
                    {
                        println "${Thread.currentThread().getName()} 线程执行异常"
                        e.printStackTrace()
                    }
                    finally 
                    {
                        addsynThreadCount()
                    }
                }
            }

            while(true)
            {
                if (threadCount == finishThreadCount)
                {
                    println "=================该批次已完成================="
                    try 
                    {
                        if (failedList?.size() > 0)
                        {
                            failedEvaluate(failedList)
                        }
                    }
                    catch(Exception e) 
                    {
                        println "失败押品异常！"
                        e.printStackTrace()
                    }
                    finally 
                    {
                        threadCount = 0
                        finishThreadCount = 0
                        failedList.clear()
                    }
                    break
                }
                Thread.sleep(1000)
            }
        }
    }

    def evaluate(List<BatchEstate> list, def em, def importBatch, def count)
    {
        def sql = new groovy.sql.Sql(dataSource)
        int excuteCounts = 0

        def SUCCESS = ExecStatusDict.findByCode('1')
        def FAILURE = ExecStatusDict.findByCode('-1')
        def EXECUTING = ExecStatusDict.findByCode('2')

        def AUTOMATIC = EvaluateDict.findByCode('1')
        def ARTIFICIAL = EvaluateDict.findByCode('2')
        def AUTOANDARTIFICIAL = EvaluateDict.findByCode('3')
        println "list size:" + list?.size()
        list?.each 
        {
            def bean = BatchEstate.get(it?.id)
            def param = [em: em,
                batchNum: importBatch.id,
                batchTotalCount: count,
                batchLevel: '户',
                city: bean?.estate?.city?.name,
                address: bean?.estate?.sourceAddress,
                floor: bean?.estate?.sourceTotalFloor,
                totalFloor: bean.estate?.sourceTotalFloor,
                square: bean?.estate?.sourceBuildArea,
                name: bean?.estate?.communityName
            ]

            excuteCounts++
            def result = [:]
            def beanMap = []
            try 
            {
                // 自动评估
                if (param.em == 1) 
                {
                    def addressSplitList = splitMatchApiService.splitMatch(param.city, param.address)
                    def communityId = addressSplitList?.match_community_id
                    if (communityId && communityId != "-1") 
                    {
                        param['name'] = addressSplitList?.match_community_name
                        result = apiService.evaluate(param)
                    } 
                    else 
                    {
                        result['code'] = "-1"
                        result['message'] = "地址拆分失败"
                    }
                }

                // 人工评估
                if (param.em == 7) 
                {
                    result = apiService.evaluate(param)
                }

                // 自动评估和人工
                if (param.em == 8) 
                {
                    def addressSplitList = splitMatchApiService.splitMatch(param.city, param.address)
                    def communityId = addressSplitList?.match_community_id
                    if (communityId && communityId != "-1") 
                    {
                        param['em'] = 1
                        param['name'] = addressSplitList?.match_community_name
                        result = apiService.evaluate(param)
                        if (result && result?.code != "0") 
                        {
                            param['beanId'] = bean?.id
                            synchronized (failedList)
                            {
                                failedList.add(param)
                            }
                            param['em'] = 9
                        }
                    }
                    else 
                    {
                        param['beanId'] = bean?.id
                        synchronized (failedList)
                        {
                            failedList.add(param)
                        }
                        result['code'] = "-1"
                        result['message'] = "地址拆分失败"
                    }
                }

                if (result) 
                {
                    if (param.em == 1) 
                    {
                        if ("0".equals(result?.code)) 
                        {
                            beanMap.add(result?.evaluateCode)
                            beanMap.add(SUCCESS?.id)
                            beanMap.add(AUTOMATIC?.id)
                            beanMap.add(bean?.id)

                            def beanMap1 = []
                            beanMap1.add(result?.unitprice)
                            beanMap1.add(result?.saleprice)
                            beanMap1.add(result?.saleprice)
                            beanMap1.add(bean?.estate?.id)
                            sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,evaluate_date=to_timestamp('${result?.evaluateDate}','YYYY-MM-DD HH24:MI:SS.MS'),modified_date=CURRENT_TIMESTAMP where id=?", beanMap
                            sql.execute "update estate set unit_price=?,total_price=?,latest_evaluate_price=?,latest_evaluate_date=to_timestamp('${result?.evaluateDate}','YYYY-MM-DD HH24:MI:SS.MS'),modified_date=CURRENT_TIMESTAMP where id=?", beanMap1
                        } 
                        else if ("-1".equals(result?.code))
                        {
                            beanMap.add(FAILURE?.id)
                            beanMap.add(AUTOMATIC?.id)
                            beanMap.add(result?.message)
                            beanMap.add(bean?.id)

                            sql.execute "update batch_estate set evaluate_statue_id=?,evaluate_type_id=?,fail_reason=?,modified_date=CURRENT_TIMESTAMP where id=?", beanMap
                        }
                        else
                        {
                            beanMap.add(result?.evaluateCode)
                            beanMap.add(AUTOMATIC?.id)
                            beanMap.add(FAILURE?.id)
                            beanMap.add(result?.message)
                            beanMap.add(bean?.id)

                            sql.execute "update batch_estate set evaluate_code=?,evaluate_type_id=?,evaluate_statue_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap
                        }
                    } 
                    else if (param.em == 7) 
                    {
                        if ("4".equals(result?.code)) 
                        {
                            beanMap.add(result?.evaluateCode)
                            beanMap.add(EXECUTING?.id)
                            beanMap.add(ARTIFICIAL?.id)
                            beanMap.add(bean?.id)
                            sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP where id=?", beanMap
                        } 
                        else 
                        {
                            beanMap.add(result?.evaluateCode)
                            beanMap.add(FAILURE?.id)
                            beanMap.add(ARTIFICIAL?.id)
                            beanMap.add(result?.message)
                            beanMap.add(bean?.id)
                            sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap
                        }
                    } 
                    // else if (param.em == 8) 
                    // {
                    //     if ("-1".equals(result?.code)) 
                    //     {
                    //         beanMap.add(FAILURE?.id)
                    //         beanMap.add(result?.message)
                    //         beanMap.add(bean?.id)

                    //         sql.execute "update batch_estate set evaluate_statue_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap
                    //     }
                    // }
                }
                else
                {
                    logService.error(rootName, bean?.batch?.id.toString(), "没有返回结果，serialNumber:" + bean?.id)
                }
            }
            catch (Exception ex) 
            {
                ex.printStackTrace()
                def beanMap1 = []
                beanMap1.add("9999")
                beanMap1.add(FAILURE?.id)
                beanMap1.add("远程API调用失败:${ex.getMessage()}")
                beanMap1.add(bean?.id)
                sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap1

                log.error("9999远程API调用失败")
            }
        }
        sql.close()
    }

    // 自动加人工评估：失败押品转人工评估
    def failedEvaluate(List<Map> failedList)
    {
        println "-----------failedEvaluate----------------"
        println failedList?.size()
        def sql = new groovy.sql.Sql(dataSource)
        def FAILURE = ExecStatusDict.findByCode('-1')
        def EXECUTING = ExecStatusDict.findByCode('2')
        def ARTIFICIAL = EvaluateDict.findByCode('2')

        def excuteCounts = 0

        int count = failedList?.size()
        failedList?.each 
        {
            excuteCounts++
            it['em'] = 7
            it['batchTotalCount'] = count
            def beanMap = []
            def result = [:]
            try
            {
                result = apiService.evaluate(it)
                println "正执行${excuteCounts}条:" + result?.evaluateCode
                if (result)
                {
                    if ("4".equals(result?.code))
                    {
                        beanMap.add(result?.evaluateCode)
                        beanMap.add(EXECUTING?.id)
                        beanMap.add(ARTIFICIAL?.id)
                        beanMap.add(it?.beanId)
                        sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP where id=?", beanMap
                    }
                    else
                    {
                        beanMap.add(result?.evaluateCode)
                        beanMap.add(FAILURE?.id)
                        beanMap.add(ARTIFICIAL?.id)
                        beanMap.add(result?.message)
                        beanMap.add(it?.beanId)
                        sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap
                    }
                }
            }
            catch (Exception ex)
            {
                ex.printStackTrace()
                def beanMap1 = []
                beanMap1.add("9999")
                beanMap1.add(FAILURE?.id)
                beanMap1.add("远程API调用失败:${ex.getMessage()}")
                beanMap1.add(it?.beabId)
                sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap1

                log.error("9999远程API调用失败")
            }
        }
        sql.close()
    }

    def updateBatchEstateCount(EstateImportBatch estateImportBatcht)
    {
        def estateImportBatch = EstateImportBatch.findById(estateImportBatcht?.id)
        def SUCCESS = ExecStatusDict.findByCode('1')
        def FAILURE = ExecStatusDict.findByCode('-1')
        def EXECUTING = ExecStatusDict.findByCode('2')
        def PARTFAILURE = ExecStatusDict.findByCode('-2')

        def successCount = BatchEstate.countByBatchAndEvaluateStatue(estateImportBatch, SUCCESS)
        def failureCount = BatchEstate.countByBatchAndEvaluateStatue(estateImportBatch, FAILURE)

        estateImportBatch.matchNum = successCount + failureCount
        if (successCount + failureCount == estateImportBatch.estateNum)
        {
            if (successCount == estateImportBatch.estateNum)
            {
                estateImportBatch.status = SUCCESS
            }
            else if (failureCount == estateImportBatch.estateNum)
            {
                estateImportBatch.status = FAILURE
            }
            else if (failureCount > 0)
            {
                estateImportBatch.status = PARTFAILURE
            }
            else
            {
                estateImportBatch.status = EXECUTING
            }
        }
        estateImportBatch.modifiedDate = new Date()
        estateImportBatch.endTime = new Date()
        estateImportBatch.save(flush:true)
    }

    // 自动评估：失败押品转人工评估
    def reEvaluateFailureEstate(Serializable batchId)
    {
        def FAILURE = ExecStatusDict.findByCode('-1')
        def EXECUTING = ExecStatusDict.findByCode('2')
        def ARTIFICIAL = EvaluateDict.findByCode('2')

        def sql = new groovy.sql.Sql(dataSource)
        EstateImportBatch importBatch = EstateImportBatch.get(batchId)
        int count = BatchEstate.countByBatchAndEvaluateStatue(importBatch, FAILURE)
        if (count > 0)
        {
            for (int i = 0; i < (count + batchLimit - 1) / batchLimit; i++)
            {
                def estateQuery = BatchEstate.where {
                    batch.id == importBatch.getId() && evaluateStatue.name == "失败"
                }
                def list = estateQuery.list([sort: 'id', offset: i * batchLimit, max: batchLimit])
                list?.each { BatchEstate bean ->
                    def param = [em: 7,
                        batchNum: bean?.batch.id,
                        batchTotalCount: count,
                        batchLevel: '户',
                        city: bean?.estate?.city?.name,
                        address: bean?.estate?.sourceAddress,
                        floor: bean?.estate?.sourceTotalFloor,
                        totalFloor: bean.estate?.sourceTotalFloor,
                        square: bean?.estate?.sourceBuildArea,
                        name: bean?.estate?.communityName
                    ]

                    def beanMap = []
                    def result = [:]
                    try
                    {
                        result = apiService.evaluate(param)
                        println "${bean?.id}:" + result?.evaluateCode
                        if (result)
                        {
                            if ("4".equals(result?.code))
                            {
                                beanMap.add(result?.evaluateCode)
                                beanMap.add(EXECUTING?.id)
                                beanMap.add(ARTIFICIAL?.id)
                                beanMap.add(bean?.id)
                                sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP where id=?", beanMap
                            }
                            else
                            {
                                beanMap.add(result?.evaluateCode)
                                beanMap.add(FAILURE?.id)
                                beanMap.add(ARTIFICIAL?.id)
                                beanMap.add(result?.message)
                                beanMap.add(bean?.id)
                                sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,evaluate_type_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap
                            }
                            sql.execute "INSERT INTO valuation_history(id, version, account_id, evaluate_code, name, valuation_date, unit_price, total_price, source_id, source_class, evaluate_status_id, evaluate_type_id, fail_reason) select nextval('hibernate_sequence'), 0, e.account_id, i.evaluate_code, e.community_name, e.latest_evaluate_date, e.unit_price, e.total_price, e.id, 'estate', i.evaluate_statue_id, i.evaluate_type_id, i.fail_reason from batch_estate i LEFT JOIN estate e on i.estate_id = e.id where e.external_id is not null and i.id = ${bean?.id}"
                        }
                    }
                    catch (Exception ex)
                    {
                        ex.printStackTrace()
                        def beanMap1 = []
                        beanMap1.add("9999")
                        beanMap1.add(FAILURE?.id)
                        beanMap1.add("远程API调用失败:${ex.getMessage()}")
                        beanMap1.add(bean?.id)
                        sql.execute "update batch_estate set evaluate_code=?,evaluate_statue_id=?,modified_date=CURRENT_TIMESTAMP,fail_reason=? where id=?", beanMap1

                        log.error("9999远程API调用失败")
                    }
                }
            }
        }
    }

    def valuationHistorySuccess(EstateImportBatch batch)
    {
        def sql = new groovy.sql.Sql(dataSource)
        def executeSql = "select e.account_id, i.evaluate_code, e.community_name, e.latest_evaluate_date, e.unit_price, e.total_price, e.id, 'estate' as source_class, i.evaluate_statue_id, i.evaluate_type_id, i.fail_reason from batch_estate i LEFT JOIN estate e on i.estate_id = e.id where e.external_id is not null and i.batch_id = ${batch?.id}"
        def result = sql.rows(executeSql)
        sql.withBatch(20, "INSERT INTO valuation_history(id, version, account_id, evaluate_code, name, valuation_date, unit_price, total_price, source_id, source_class, evaluate_status_id, evaluate_type_id, fail_reason) VALUES (nextval('hibernate_sequence'),0,?1.account_id,?1.evaluate_code,?1.community_name,?1.latest_evaluate_date,?1.unit_price,?1.total_price,?1.id,?1.source_class,?1.evaluate_statue_id,?1.evaluate_type_id,?1.fail_reason)") { ps ->
            result?.each
            {
                try
                {
                    ps.addBatch(it)
                }
                catch (Exception e)
                {
                    println "valuationHistorySuccess error"
                    e.printStackTrace()
                    logService.error(rootName,batch?.id,"valuationHistorySuccess 失败，INFO:" + it)
                }
            }
        }
        sql.close()
    }
}